local UtilityExcel		= require("excel.Skill").Utility
local SkillExcel		= require("excel.Skill").Skill
local Util				= require("common.Util")
local Timer				= require("core.Timer")
local Obj				= require("core.Obj")
local Msg				= require("core.Msg")
local Notify			= require("skill.Notify")
local Conditions		= require("skill.Conditions")
local Callback			= require("skill.Callback")
local CoolDown			= require("skill.CoolDown")
local FixPosition		= require("skill.FixPosition")
local Loss				= require("skill.Loss")
local SkillDefine		= require("skill.Define")
local Cmds				= require("buffer.Cmds")
local Node          = require("skill.Node")
local Barrels       = require("skill.Barrels")
local random			= math.random
local getDir			= Util.getDir
local ERR_SKILL_LOSS	= SkillDefine.ERR_SKILL_LOSS
local ERR_SKILL_USE		= SkillDefine.ERR_SKILL_USE
local QiliaoLogic = require("qiliao.QiliaoLogic")

local interval  = 150
local size    = 27  -- 最大间隔 150(最小间隔) * 27 = 4050毫秒
local runSize = 10  -- 执行最大列
timer = timer or Barrels.init(interval, size)
tempList = tempList or Barrels.init(interval, runSize) --执行列

count = count or 0
usedTime = usedTime or 0

gdb_skill = _G.gdb.gdb_skill
function onTimer(during)
    -- 处理上次报错剩余的列表技能
    for i=1, runSize do
        for j=1, tempList[i][0] do
          local value = tempList[i][j]
          tempList[i][j] = nil
          if value then
            value:onEnd()
          end
        end
        tempList[i][0] = 0
    end
    local cnt = 0
    if during % interval < interval/2 then
        cnt = math.floor(during/interval)
    else
        cnt = math.ceil(during/interval)
    end
    if cnt > runSize then -- 最高执行10个列
        cnt = runSize
    end
--    if cnt > 1 then
--        print("skill.onTimer:", cnt)
--    end
    local totleRunCnt = 0
    for i=1, cnt do
      timer.offset = timer.offset + 1     -- 时钟偏移
      local index  = timer.offset % timer.size
      if index==0 then index = timer.size end
      timer.offset = index
      tempList[i][0]        = 0
      timer[index], tempList[i] = tempList[i], timer[index]
      totleRunCnt = totleRunCnt + tempList[i][0]
    end
    
    gdb_skill(totleRunCnt)
    -- 时间结束
    for i=1, cnt do
        for j = 1, tempList[i][0] do
          local value = tempList[i][j]
          tempList[i][j] = nil
          value:onEnd()
        end
        tempList[i][0] = 0
    end    
end


-- 初始化
function init(human)
    local userData			 = getUserData(human)
    userData.cooldown	 = human.db.cooldown
end


function link(obj, skillID, skillConfig)
    local userData      = getUserData(obj)
    if #skillConfig.link > 0 then
        if not skillConfig.linkparent and obj.bufAttr.noSkill then
            return        --如果是第一招，然后中了眩晕不走后面的连招了
        end
        -- 设置公共冷却时间为前摇时间
        userData.normalSkillCommonCooldown = skillConfig.qianyao_sec > 0 and skillConfig.qianyao_sec*1000 or 500
        if obj.obj_type == Obj.TYPE_HUMAN then
            Notify.GC_SKILL_LINK(obj, skillID, skillConfig.link[1][1], skillConfig.link[1][2])
        end
    elseif skillConfig.linkparent and #skillConfig.link == 0 then  --最后一招
        userData.normalSkillCommonCooldown = skillConfig.qianyao_sec > 0 and skillConfig.qianyao_sec*1000 or 500
        if obj.obj_type == Obj.TYPE_HUMAN then
            Notify.GC_SKILL_LINK(obj, skillID)
        end
    end
end

--- ***** 外部接口 ********

-- 使用技能( 冷却/消耗 限制)

function use(obj, skillID, tx, ty, x, y, target, level, from_client)
    local clskillcd   = obj.clskillcd
    local result	  = Conditions.canUseSkill(obj, skillID, target)
    if result ~=true and not clskillcd then return result end
    local skillConfig = SkillExcel[skillID]
    Callback.onUse(obj, skillID, skillConfig, x, y, target)
    if not level then level = 1 end

    local isFail
    tx, ty, isFail = FixPosition.handler(obj, skillID, skillConfig, x, y, tx, ty, target, level)
    if isFail then return ERR_SKILL_USE end

    local newSkillID

    result, newSkillID = add(obj, skillID, tx, ty, x, y, target, level, true)
    if (result ~=true and not clskillcd) or not newSkillID then return result end

    if not from_client or  not skillConfig.linkparent then
        CoolDown.doCoolDown(obj, skillID, skillConfig)
    end
    Notify.GC_SKILL_FIRE(obj, skillID, newSkillID, tx, ty, x, y, target)
    if obj.obj_type == Obj.TYPE_HUMAN then
      QiliaoLogic.onUse(obj, skillID)
    end
    if from_client or obj.obj_type == Obj.TYPE_MONSTER then
        link(obj, skillID, skillConfig)
    end
    return true
end


-- 添加技能( 消耗 限制)
function add(obj, skillID, tx, ty, x, y, target, level, noFire)
    local result = Conditions.canAddSkill(obj, skillID, target)

    if result	~=true  then return result end
    if not Loss.handler(obj, SkillExcel[skillID].loss, skillID) then return ERR_SKILL_LOSS end

    local newSkillID = getCallSkillID(obj, skillID)
    local sceneID	 = obj.scene_id

    local id, uid = obj.id, obj.uid
    local tid, tuid
    if target then
        tid = target.id
        tuid = target.uid
    end
    if not obj.dir or tx ~= x or ty ~= y then
        obj.dir = getDir(tx - x, ty - y)
    end    
    local node = Node.getInstance()
    if not node then
        assert()
    end
    node.skillID   = skillID
    node.sceneID   = sceneID
    node.id      = id
    node.uid     = uid
    node.level     = level or 1
    node.mouseX    = tx
    node.mouseY    = ty
    node.tx      = tx
    node.ty      = ty
    node.x       = x
    node.y       = y
    node.dir     = obj.dir
    node.tid     = tid
    node.tuid    = tuid
    node.createTime  = Timer.now
    node:onStart()

    local skillConfig = SkillExcel[newSkillID]

    if not noFire then
        CoolDown.calcCoolDown(obj, skillID)
        Notify.GC_SKILL_FIRE(obj, skillID, newSkillID, tx, ty, x, y, target)
    elseif skillConfig.fireSound~=0 then
        local mm = Msg.gc.GC_SKILL_FIRE_SOUND
        mm.fireSound = skillConfig.fireSound
        Obj.sendArea(mm, sceneID, x, y)
    end
    return true, newSkillID
end

function addHp(obj, hp, senderID, heavyHit)
    local currHp = Obj.getHp(obj)
    if currHp<1 or hp==0 then return end	-- 目标死亡
    if hp>0 then
        hp = Cmds.passive.hpRate(obj, hp)
        Obj.setHp(obj, currHp+hp)
        Notify.GC_SKILL_HIT(obj, senderID, -hp, heavyHit)
    else
        local sender = Obj.objs[senderID]
        Callback.onAttack(sender, obj, nil, nil, true, nil, nil, -hp)
    end
end

local tempSkillUserData = { skillID=0, actionIndex=0, skillLastTime=0, normalSkillLastTime=0, lastSkillID=0, hitQuake=0, countAtk={}, subList={}, coolDownList={}, coolDownCount={} }
function sameUserData()
    tempSkillUserData.skillID=0
    tempSkillUserData.actionIndex=0
    tempSkillUserData.skillLastTime=0
    tempSkillUserData.normalSkillLastTime=0
    tempSkillUserData.lastSkillID=0
    tempSkillUserData.hitQuake=0

    local countAtk		= tempSkillUserData.countAtk
    local subList		= tempSkillUserData.subList
    local coolDownList	= tempSkillUserData.coolDownList
    local coolDownCount = tempSkillUserData.coolDownCount

    for k in pairs(countAtk) do
        countAtk[k] = nil
    end

    for k in pairs(subList) do
        subList[k] = nil
    end

    for k in pairs(coolDownList) do
        coolDownList[k] = nil
    end

    for k in pairs(coolDownCount) do
        coolDownCount[k] = nil
    end
    return tempSkillUserData
end

function getUserData(obj)
    --if (obj.monster_id and obj.owner) or not canAttack(obj) then	-- 怪物有主人 或者 AOE TRAP
    --	if obj.db then
    --		assert()
    --	end
    --	obj.skillUserData = sameUserData()
    --end

    if not obj.skillUserData then
        obj.skillUserData = { skillID=0, actionIndex=0, skillLastTime=0, normalSkillLastTime=0, normalSkillCommonCooldown= 500, lastSkillID=0, hitQuake=0, countAtk={}, subList={}, cooldown={}, cooldownLen = 0 }
    end
    if obj.obj_type == Obj.TYPE_HUMAN then
        obj.skillUserData.cooldownLen = obj.db.cooldownLen
    end

    return obj.skillUserData
end

function getCallSkillID(obj, skillID)
    local userData  = getUserData(obj)
    local v			= userData.countAtk[skillID]
    if not v then return skillID end

    local now = Timer.now

    if now - v.lastTime > 3000 then
        v.count = 1
        v.lastTime = now
        return skillID
    end

    local maxCount = v.maxCount
    if maxCount and v.count>= maxCount then
        v.count = 1
        return v.skillID
    end

    return skillID
end

-- 处理不同播放动作
function getAction(obj, skillID, config)
    local userData		 = getUserData(obj)
    if userData.skillID ~= skillID then	userData.skillID = skillID end

    local actionIndex
    if #config.actionID > 1 and  random()>0.6 then
        actionIndex = 2
    else
        actionIndex = 1
    end
    userData.actionIndex = actionIndex
    return config.actionID[actionIndex], actionIndex
end

function getAttackLen(skillID)
    local config = SkillExcel[skillID]
    return UtilityExcel[config.mainUtility].mouseToSelf
end

function fixDirAttackType(obj, skillID, skillConfig, x, y, tx, ty)
    return FixPosition.fixDirAttackType(obj, skillID, skillConfig, x, y, tx, ty)
end
